p2m_access_t p2ma;
mem_event_request_t *req;
int rc;
+ unsigned long eip = guest_cpu_user_regs()->eip;
/* First, handle rx2rw conversion automatically.
* These calls to p2m->set_entry() must succeed: we have the gfn
}
}
+ /* The previous mem_event reply does not match the current state. */
+ if ( v->arch.mem_event.gpa != gpa || v->arch.mem_event.eip != eip )
+ {
+ /* Don't emulate the current instruction, send a new mem_event. */
+ v->arch.mem_event.emulate_flags = 0;
+
+ /*
+ * Make sure to mark the current state to match it again against
+ * the new mem_event about to be sent.
+ */
+ v->arch.mem_event.gpa = gpa;
+ v->arch.mem_event.eip = eip;
+ }
+
+ if ( v->arch.mem_event.emulate_flags )
+ {
+ hvm_mem_event_emulate_one((v->arch.mem_event.emulate_flags &
+ MEM_EVENT_FLAG_EMULATE_NOWRITE) != 0,
+ TRAP_invalid_op, HVM_DELIVER_NO_ERROR_CODE);
+
+ v->arch.mem_event.emulate_flags = 0;
+ return 1;
+ }
+
*req_ptr = NULL;
req = xzalloc(mem_event_request_t);
if ( req )
v = d->vcpu[rsp.vcpu_id];
+ /* Mark vcpu for skipping one instruction upon rescheduling. */
+ if ( rsp.flags & MEM_EVENT_FLAG_EMULATE )
+ {
+ xenmem_access_t access;
+ bool_t violation = 1;
+
+ if ( p2m_get_mem_access(d, rsp.gfn, &access) == 0 )
+ {
+ switch ( access )
+ {
+ case XENMEM_access_n:
+ case XENMEM_access_n2rwx:
+ default:
+ violation = rsp.access_r || rsp.access_w || rsp.access_x;
+ break;
+
+ case XENMEM_access_r:
+ violation = rsp.access_w || rsp.access_x;
+ break;
+
+ case XENMEM_access_w:
+ violation = rsp.access_r || rsp.access_x;
+ break;
+
+ case XENMEM_access_x:
+ violation = rsp.access_r || rsp.access_w;
+ break;
+
+ case XENMEM_access_rx:
+ case XENMEM_access_rx2rw:
+ violation = rsp.access_w;
+ break;
+
+ case XENMEM_access_wx:
+ violation = rsp.access_r;
+ break;
+
+ case XENMEM_access_rw:
+ violation = rsp.access_x;
+ break;
+
+ case XENMEM_access_rwx:
+ violation = 0;
+ break;
+ }
+ }
+
+ v->arch.mem_event.emulate_flags = violation ? rsp.flags : 0;
+ }
+
/* Unpause domain */
if ( rsp.flags & MEM_EVENT_FLAG_VCPU_PAUSED )
mem_event_vcpu_unpause(v);
#include "io/ring.h"
/* Memory event flags */
-#define MEM_EVENT_FLAG_VCPU_PAUSED (1 << 0)
-#define MEM_EVENT_FLAG_DROP_PAGE (1 << 1)
-#define MEM_EVENT_FLAG_EVICT_FAIL (1 << 2)
-#define MEM_EVENT_FLAG_FOREIGN (1 << 3)
-#define MEM_EVENT_FLAG_DUMMY (1 << 4)
+#define MEM_EVENT_FLAG_VCPU_PAUSED (1 << 0)
+#define MEM_EVENT_FLAG_DROP_PAGE (1 << 1)
+#define MEM_EVENT_FLAG_EVICT_FAIL (1 << 2)
+#define MEM_EVENT_FLAG_FOREIGN (1 << 3)
+#define MEM_EVENT_FLAG_DUMMY (1 << 4)
+/*
+ * Emulate the fault-causing instruction (if set in the event response flags).
+ * This will allow the guest to continue execution without lifting the page
+ * access restrictions.
+ */
+#define MEM_EVENT_FLAG_EMULATE (1 << 5)
+/*
+ * Same as MEM_EVENT_FLAG_EMULATE, but with write operations or operations
+ * potentially having side effects (like memory mapped or port I/O) disabled.
+ */
+#define MEM_EVENT_FLAG_EMULATE_NOWRITE (1 << 6)
/* Reasons for the memory event request */
#define MEM_EVENT_REASON_UNKNOWN 0 /* typical reason */